﻿using System.Reflection;
using Microsoft.EntityFrameworkCore;
using ZeroSum.Attributes;
using ZeroSum.Domain.Entities;
using ZeroSum.Extend.EFCore.MySql.Attributes;
using ZeroSum.Extensions;
using ZeroSum.Helpers;

namespace ZeroSum.Extend.EFCore.MySql.EntityFrameworks;

public class ZeroSumDbContext : DbContext
{
    public ZeroSumDbContext(DbContextOptions options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        var assemblies = new List<Assembly>();
        assemblies.AddRange(AssemblyHelper.GetProjectAssemblies());
        assemblies.AddRange(AssemblyHelper.GetPackageAssemblies(nameof(ZeroSum)));


        // 扫描需要注册的实体类（通过实体标签）
        RegisterEntityByAttribute(assemblies, modelBuilder);

        // 扫描需要注册的实体类（通过Map）
        RegisterEntityByMap(assemblies, modelBuilder);


        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            if (entityType.ClrType.IsAssignableTo(typeof(ISoftDelete)))
                modelBuilder.Entity(entityType.ClrType).AddQueryFilter<ISoftDelete>(e => !e.IsDeleted);

        base.OnModelCreating(modelBuilder);
    }

    /// <summary>
    ///     通过IEntityTypeConfiguration来注册实体
    /// </summary>
    /// <param name="assemblies"></param>
    /// <param name="builder"></param>
    private void RegisterEntityByMap(IEnumerable<Assembly> assemblies, ModelBuilder builder)
    {
        foreach (var assembly in assemblies) builder.ApplyConfigurationsFromAssembly(assembly);
    }

    /// <summary>
    ///     通过EntityAttribute来扫描多个程序集中需要注册的实体类
    /// </summary>
    /// <param name="assemblies"></param>
    /// <param name="builder"></param>
    /// <exception cref="NotImplementedException"></exception>
    private void RegisterEntityByAttribute(IEnumerable<Assembly> assemblies, ModelBuilder builder)
    {
        foreach (var assembly in assemblies) RegisterEntityByAttribute(assembly, builder);
    }

    /// <summary>
    ///     通过EntityAttribute来扫描单个程序集中需要注册的实体类
    /// </summary>
    /// <param name="assembly"></param>
    /// <param name="builder"></param>
    private void RegisterEntityByAttribute(Assembly assembly, ModelBuilder builder)
    {
        var types = assembly.GetTypes().Where(t => t.HasAttribute<EntityAttribute>());
        foreach (var type in types)
        {
            var mode = builder.Entity(type);
            var propertyInfos = type.GetProperties()
                .Where(x => x.GetCustomAttribute<HasValueGeneratorAttribute>() != null);
            foreach (var propertyInfo in propertyInfos)
            {
                var hasValueGeneratorAttribute = propertyInfo.GetAttribute<HasValueGeneratorAttribute>();
                mode.Property(propertyInfo.PropertyType, propertyInfo.Name).ValueGeneratedOnAdd()
                    .HasValueGenerator(hasValueGeneratorAttribute!.ValueGeneratorType);
            }
        }
    }
}